{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[this doc on github](https://github.com/dotnet/interactive/tree/main/samples/notebooks/fsharp)\n",
    "\n",
    "# Introduction to F# #\n",
    "\n",
    "F# is an [open-source, cross-platform functional programming language](http://aka.ms/fsharphome) for .NET.\n",
    "\n",
    "F# has features and idioms to support functional programming while also offering clean interop with C# and existing .NET codebases and systems. It can use anything in the [NuGet](https://www.nuget.org/) ecosystem, as this notebook will also demonstrate."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## F# basics\n",
    "\n",
    "Let's start with some simple arithmetic:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "(12/4 + 5 + 7) * 4 - 18"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Arithmetic is nice, but there's so much more you can do. Here's how you can generate some data using the `[start .. end]` range syntax:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let numbers = [0 .. 10]\n",
    "numbers"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can use slices with the `.[start .. end]` syntax to slice a subset of the data you just generated:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "// Take the numbers from 2nd index to the 5th\n",
    "numbers.[2..5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And you can use indexer syntax (`.[index]`) to access a single value:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "numbers.[3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Functions in F# #\n",
    "\n",
    "Since F# is a functional language, functions are one of the first things to learn. You do that with the `let` keyword. F#, like Python, uses indentation to define code blocks:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let sampleFunction x =\n",
    "    2*x*x - 5*x + 3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "F# uses type inference to figure out types for you. But if needed, you can specify types explicitly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let sampleFunction' (x: int) =\n",
    "    2*x*x - 5*x + 3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When calling F# functions, parentheses are optional:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "sampleFunction 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "sampleFunction' 12"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can define and compose F# functions easily:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let negate x = -x\n",
    "let square x = x * x\n",
    "let print x = printfn \"The number is %d\" x\n",
    "\n",
    "let squareNegateThenPrint x =\n",
    "    print (negate (square x))\n",
    "    \n",
    "squareNegateThenPrint 5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The pipeline operator `|>` is used extensively in F# code to chain functions and arguments together. It helps readability when building functional \"pipelines\":"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "// Redefine the function with pipelines\n",
    "let squareNegateThenPrint x =\n",
    "    x\n",
    "    |> square\n",
    "    |> negate\n",
    "    |> print\n",
    "\n",
    "squareNegateThenPrint 5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Strings, tuples, lists, and arrays\n",
    "\n",
    "Strings in F# use `\"` quotations. You can concatenate them with the `+` operator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let s1 = \"Hello\"\n",
    "let s2 = \"World\"\n",
    "\n",
    "s1 + \", \" + s2 + \"!\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can use triple-quoted strings (`\"\"\"`) if you want to have a string that contains quotes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "\"\"\"A triple-quoted string can contain quotes \"like this\" anywhere within it\"\"\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Tuples\n",
    "\n",
    "Tuples are simple combinations of data items into a single value. The following defines a tuple of an integer, string, and double:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "(1, \"fred\", Math.PI)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also create `struct` tuples when you have performance-sensitive environments:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    }
   },
   "outputs": [],
   "source": [
    "struct (1, Math.PI)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Lists\n",
    "\n",
    "Lists are linear sequences of values of the same type. In fact, you've already seen them above when we generated some numbers!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    }
   },
   "outputs": [],
   "source": [
    "[0 .. 10]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can use list comprehensions to generate more advanced data programmatically:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let thisYear = DateTime.Now.Year\n",
    "\n",
    "let fridays =\n",
    "    [\n",
    "        for month in 1 .. 10 do\n",
    "            for day in 1 .. DateTime.DaysInMonth(thisYear, month) do\n",
    "                let date = DateTime(thisYear, month, day)\n",
    "                if date.DayOfWeek = DayOfWeek.Friday then\n",
    "                    date.ToShortDateString()\n",
    "    ]\n",
    "\n",
    "// Get the first five fridays of this year\n",
    "fridays\n",
    "|> List.take 5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since you can slice lists, the first five fridays could also be done like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "fridays.[..4]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Arrays\n",
    "\n",
    "Arrays are very similar to lists. A key difference is that array internals are mutable. They also have better performance characteristics than lists."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let firstTwoHundred = [| 1 .. 200 |]\n",
    "firstTwoHundred.[197..]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Processing lists and arrays is typically done by built-in and custom functions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "// Filter the previous list of numbers and sum their squares.\n",
    "firstTwoHundred\n",
    "|> Array.filter (fun x -> x % 3 = 0)\n",
    "|> Array.sumBy (fun x -> x * x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Types\n",
    "\n",
    "Although F# is succinct, it actually uses static typing! Types are central to F# programming, especially when you want to model more complicated data to manipulate later in a program."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Records\n",
    "\n",
    "Record types are used to combine different kinds of data into an aggregate. They cannot be `null` and come with default comparison and equality."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "type ContactCard =\n",
    "    { Name: string\n",
    "      Phone: string\n",
    "      ZipCode: string }\n",
    "\n",
    "// Create a new record\n",
    "{ Name = \"Alf\"; Phone = \"(555) 555-5555\"; ZipCode = \"90210\" }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this notebook environment, records print with a table-like output by default.\n",
    "\n",
    "You can access record labels with `.`-notation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let alf = { Name = \"Alf\"; Phone = \"(555) 555-5555\"; ZipCode = \"90210\" }\n",
    "alf.Phone"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Records are comparable and equatable:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "// Create another record\n",
    "let ralph = { Name = \"Ralph\"; Phone = \"(123) 456-7890\"; ZipCode = \"90210\" }\n",
    "\n",
    "// Check if they're equal\n",
    "alf = ralph"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You'll find yourself writing functions that operate on records all the time:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let showContactCard contact =\n",
    "    contact.Name + \" - Phone: \" + contact.Phone + \", Zip: \" + contact.ZipCode\n",
    "    \n",
    "showContactCard alf"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Discriminated Unions\n",
    "\n",
    "Discriminated Unions (often called DUs) provide support for values that can be one of a number of named cases. These cases can be completely different from one another.\n",
    "\n",
    "In the following example, we combine records with a discriminated union:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "type Shape =\n",
    "    | Rectangle of width: float * length: float\n",
    "    | Circle of radius: float\n",
    "    | Prism of width: float * height: float * faces: int\n",
    "    \n",
    "let rect = Rectangle(length = 1.3, width = 10.0)\n",
    "let circ = Circle (1.0)\n",
    "let prism = Prism(width = 5.0, height = 2.0, faces = 3)\n",
    "        \n",
    "prism"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pattern matching\n",
    "\n",
    "The best way to work with DUs is pattern matching. Using the previously-defined type definitions, we can model getting the height of a shape."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let height shape =\n",
    "    match shape with\n",
    "    | Rectangle(width = h) -> h\n",
    "    | Circle(radius = r) -> 2.0 * r\n",
    "    | Prism(height = h) -> h\n",
    "    \n",
    "let rectHeight = height rect\n",
    "let circHeight = height circ\n",
    "let prismHeight = height prism\n",
    "\n",
    "printfn \"rect is %0.1f, circ is %0.1f, and prism is %0.1f\" rectHeight circHeight prismHeight"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can pattern match on more than just discriminated unions. Here we write a recursive function with `rec` to process lists:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "// See if x is a multiple of n\n",
    "let isPrimeMultiple n x =\n",
    "    x = n || x % n <> 0\n",
    "    \n",
    "// Process lists recursively.\n",
    "// '[]' means the empty list.\n",
    "// 'head' is an item in the list.\n",
    "// 'tail' is the rest of the list after 'head'.\n",
    "let rec removeMultiples ns xs =\n",
    "    match ns with\n",
    "    | [] -> xs\n",
    "    | head :: tail ->\n",
    "        xs\n",
    "        |> List.filter (isPrimeMultiple head)\n",
    "        |> removeMultiples tail\n",
    "        \n",
    "let getPrimesUpTo n =\n",
    "    let max = int (sqrt (float n))\n",
    "    removeMultiples [2 .. max] [1 .. n]\n",
    "    \n",
    "// Primes up to 25\n",
    "getPrimesUpTo 25"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Options\n",
    "\n",
    "A built-in DU type is the F# option type. It is used prominently in F# code. Options can either be `Some` or `None`, and they're best used when you want to account for when there may not be a value."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let keepIfPositive a =\n",
    "    if a > 0 then\n",
    "        Some a\n",
    "    else \n",
    "        None\n",
    "        \n",
    "keepIfPositive 12"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Options are often used when searching for values. Here's how you can incorporate them into list processing:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let rec tryFindMatch predicate lst =\n",
    "    match lst with\n",
    "    | [] -> None\n",
    "    | head :: tail ->\n",
    "        if predicate head then\n",
    "            Some head\n",
    "        else\n",
    "            tryFindMatch predicate tail\n",
    "          \n",
    "let greaterThan100 x = x > 100\n",
    "\n",
    "tryFindMatch greaterThan100 [25; 50; 100; 150; 200]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Parallel Programming\n",
    "\n",
    "For more CPU-intensive tasks, you can take advantage of built-in parallelism:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "#!time\n",
    "\n",
    "let bigArray = [| 0 .. 100_000 |]\n",
    "\n",
    "let rec fibonacci n = if n <= 2 then n else fibonacci (n-1) + fibonacci (n-2)\n",
    "\n",
    "// We'll use the '%A' print formatter for F# constructs for these results, since they are enormous\n",
    "let results =\n",
    "    bigArray\n",
    "    |> Array.Parallel.map (fun n -> fibonacci (n % 25))\n",
    "\n",
    "printfn \"%A\" results"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Because F# functions are first-class values, you can trivially do things like initialize expensive functions in parallel with the `Array.Parallel` module. This is quite common in numerics-intensive F# code.\n",
    "\n",
    "Here's an example where you can compute as many fibonacci numbers as there are threads in your current process. The `#!time` magic command shows the wall-clock time it took to perform the operation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "#!time\n",
    "\n",
    "// Restrict the number of threads to a max of 25\n",
    "let nThreads = min 25 Environment.ProcessorCount\n",
    "    \n",
    "Array.Parallel.init nThreads fibonacci"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It's also worth noting how much faster the second cell ran than the first one. This is because it doesn't use call `printfn` with the `%A` formatter. Although this kind of formatting is very convenient in F#, it comes at a performance cost!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Learn more\n",
    "\n",
    "There are a lot of learning resources for F# that go far beyond this notebook.\n",
    "\n",
    "Check out the [F# docs homepage](https://docs.microsoft.com/dotnet/fsharp/) for an organized set of learning material.\n",
    "\n",
    "To learn more about using F# with this Jupyter kernel, we recommmend the following notebooks:\n",
    "\n",
    "* [The F# notebook programming model](Docs/Programming-model.ipynb)\n",
    "* [Displaying output](Docs/Displaying-output.ipynb)\n",
    "* [Importing packages](Docs/Importing-packages.ipynb)\n",
    "* [Plotting with Xplot](Docs/Plotting-with-Xplot.ipynb)\n",
    "\n",
    "For more advanced samples, check out the following:\n",
    "\n",
    "* [HousingML](Samples/HousingML.ipynb)* [Getting started with data frames](Samples/DataFrame-Getting-Started.ipynb)* [GitHub Repo Statistics](Samples/Repo-Statistics.ipynb)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".NET (F#)",
   "language": "F#",
   "name": ".net-fsharp"
  },
  "language_info": {
   "file_extension": ".fs",
   "mimetype": "text/x-fsharp",
   "name": "C#",
   "pygments_lexer": "fsharp",
   "version": "4.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}